(function (vue, name) {
  vue.component(name, {
    template: '<div class="yu-workflow" style="border: 1px solid #dadce0">\
        <slot name="dialog"></slot>\
        <div :id="splashID" :style="splashStyle" style="opacity:1">\
          <img src="libs/mxgraph/images/loading.gif">\
        </div>\
        <el-row style="padding:0">\
          <el-col :span="24">\
            <div :id="containerID" class="yu-workflow-container" style="position:relative;" :style="{height:height +\'px\', transition: \'all 0.3s\'}"></div>\
          </el-col>\
        </el-row>\
      </div>',
    props: {
      height: { //画布高度
        type: Number,
        default: 500
      },
      workType: { // 可选值workflow:流程图, serviceModule：服务模块部署
        type: String,
        default: 'workflow'
      },
      status: { // 默认编辑状态，可以增加删除子元素，show,展示状态，不能新增删除子元素，可以启动或暂停
        type: String,
        default: 'edit'
      },
      toolbarClass: { // 工具条样式
        type: String,
        default: ''
      },
      sidebarItems: {
        type: Array,
        default: function(){
          return [];
        }
      },
      showSidebar: { // 工具条样式
        type: Boolean,
        default: true
      },
      showToolbar: { // 左侧工具调
        type: Boolean,
        default: true
      },
      viewType: { // 预览类型， view时不可编辑,默认会将lock状态设置为true
        type: String,
        default: ''
      },
      lock:{ // 是否锁定graph
        type: Boolean,
        default: false
      },
      isdrawjobflowgraph: { // 是否是绘制流程图页面
        type: Boolean,
        default: false
      }
    },
    computed: {
      splashStyle: function(){
        return {
          width: '100%',
          height: '100%',
          'line-height': this.height + 'px',
          background: 'white',
          position: 'absolute',
          top: '0px',
          left: '0px',
          opacity: this.opacitySplash,
          'z-index': 11,
          'text-align': 'center',
          'vertical-align': 'middle'
        }
      }
    },
    data: function () {
      return {
        locked: this.viewType === 'view' || this.lock,
        editorui: null,
        graph: null,
        editor: null,
        outline: null,
        toolbar: null,
        layoutAnimate: true,
        layoutCount: 0,
        size: null,
        opacitySplash: 1,
        containerID: '',
        splashID: '',
        toolbarID: '',
        outlineID: '',
        sidebarID: '',
        container: null,
        outlineHiddenClick: false,
        isLayouting: false, // 是否正在自动布局
        isIniting: true, // 是否正在初始化
        currentCell: null,
        scale: 6
      };
    },
    created: function () {
      window.mxBasePath = 'libs/mxgraph/';
      this.containerID = 'graph_' + new Date().getTime();
      this.splashID = 'splash_' + new Date().getTime();
      this.toolbarID = 'toolbar_' + new Date().getTime();
      this.outlineID = 'outline_' + new Date().getTime();
      this.sidebarID = 'sidebar_' + new Date().getTime();
    },
    mounted: function () {
      var _this = this;
      try {
        if (!mxClient.isBrowserSupported()) {
          mxUtils.error('Browser is not supported!', 200, false);
        } else {
          this.initEditor();
        }
      } catch (e) {
        this.hideSplash();
        mxUtils.alert('初始化mxGraph报错:' + e.message);
      }
    },
    destroyed: function() {
      this.clearGraph();
      this.editorui.actions.outlineWindow && this.editorui.actions.outlineWindow.destroy();
      // mxStyleRegistry.putValue('customPerimeter', null);
      // mxPerimeter.CustomPerimeter = null;
      this.graph.getTooltipForCell = null;
      mxWindow.activeWindow.setLocation = null;
      mxWindow.activeWindow.destroy();
      this.graph.destroy();
      this.graph = null;
      this.editorui = null;
      this.editor = null;
      // mxListenerLists && mxListenerLists.length && mxListenerLists.forEach(function(l){
      //   mxEvent.removeListener(l.element, l.name, l.f);
      //   l.f = null;
      // })
      // mxListenerLists = null;
      this.$off();
    },
    methods: {
      hideSplash: function(){
        var _this = this;
        // Fades-out the splash screen
        var splash = document.getElementById(_this.splashID);
        
        if (splash != null) {
          try {
            mxEvent.release(splash);
            mxEffects.fadeOut(splash, 10, true);
          } catch (e) {
            splash.parentNode.removeChild(splash);
          }
        }
      },
      /**
       * @description 获取graph编辑器
       * @param {String} xml xml文件路径
       */
      _getGraphEditor: function (xml) {
        return new mxEditor(document.getElementById(this.containerID));
      },
      setGraphStylesheet:function(){
        // 加载样式列表
        var node = mxUtils.load(STYLE_PATH + '/default.xml').getDocumentElement();
        if (node != null) {
          var dec = new mxCodec(node.ownerDocument);
          dec.decode(node, this.graph.getStylesheet());
        }
      },
      initEditor: function() {
        var _this = this;
        var container = document.getElementById(this.containerID);
        mxResources.loadDefaultBundle = false;
        var bundle = mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) || mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage);
        // 自定义节点的锚点
        mxPerimeter.CustomPerimeter = function (bounds, vertex, next, orthogonal) {
          var cx = bounds.getCenterX();
          var cy = bounds.getCenterY();
          var dx = next.x - cx;
          var dy = next.y - cy;
          var alpha = Math.atan2(dy, dx);
          var p = new mxPoint(0, 0);
          var pi = Math.PI;
          var pi2 = Math.PI / 2;
          var beta = pi2 - alpha;
          var t = Math.atan2(bounds.height, bounds.width);
          var margin = 0; // 锚点边距
          if (alpha < -pi + t || alpha > pi - t) {
            // Left edge 左侧锚点
            p.x = bounds.x - margin;
            p.y = dy === 0 ? cy - bounds.width * Math.tan(alpha) / 2 : cy;
          } else if (alpha < -t) {
            // Top Edge 顶部锚点
            p.y = bounds.y - margin;
            p.x = (dx === 0 ? cx - bounds.height * Math.tan(beta) / 2 : cx);
          } else if (alpha < t) {
            // Right Edge 右侧锚点
            p.x = bounds.x + bounds.width + margin;
            p.y = dy === 0 ? cy + bounds.width * Math.tan(alpha) / 2 : cy;
          } else {
            // Bottom Edge 底部锚点
            p.y = bounds.y + bounds.height + margin;
            p.x = dx === 0 ? cx + bounds.height * Math.tan(beta) / 2 : cx;
          }
          return p;
        };
        if (!mxStyleRegistry.getValue('customPerimeter')) {
          mxStyleRegistry.putValue('customPerimeter', mxPerimeter.CustomPerimeter);
        }
        // Fixes possible asynchronous requests
        mxUtils.getAll([bundle, STYLE_PATH + '/default.xml'], function(xhr)	{
          _this.opacitySplash = 0.6;
          // Adds bundle text to resources
          mxResources.parse(xhr[0].getText());
          // Configures the default graph theme
          var themes = new Object();
          themes[Graph.prototype.defaultThemeName] = xhr[1].getDocumentElement();
          // 所有节点
          // Main
          _this.editor = new Editor(false, themes);
          _this.editorui = new EditorUi(_this.editor, container, undefined, {
            menuBar: false,
            format: false,
            toolbar: _this.showToolbar,
            sidebarFooter: false,
            sidebar: _this.showSidebar ? {
              items: _this.sidebarItems
            }: false
          });
          _this.graph = _this.editorui.editor.graph;
          _this.graph.cellsEditable = false; // 禁用编辑(连线的value)
          _this.graph.splitEnabled = false;  // 禁用分隔连线 (mxEvent.SPLIT_EDGE事件, 拖动节点到到连线之间, 连线会分隔 自动接上节点)
          _this.graph.keepEdgesInBackground = true;  // Specifies if edges should appear in the background regardless of their order in the model. If keepEdgesInForeground and keepEdgesInBackground are both true then the normal order is applied.  Default is false.
          // _this.graph.allowNegativeCoordinates = false; // 是否允许负坐标
          _this.editorui.actions.get('outline').funct();
          _this.graph.toggleCellStyles(mxConstants.STYLE_EDITABLE, 0);
          _this.$emit('init');
          _this.graph.relayout = function() {
            // var l = ['layout','circleLayout', 'organicLayout','stackLayout','radialTreeLayout','partitionLayout','parallelEdgeLayout','fastOrganicLayout','edgeLabelLayout', 'compactTreeLayout'];
            var l = ['layout'];
            var layout = l[_this.layoutCount ++ % l.length];
            _this[layout]();
          };
          _this.graph.saveData = function() {
            _this.resetCurrentCellStyle();
            _this.$emit('save');
          }
          // 选择上游(依赖)节点
          _this.graph.selectLastCell = function() {
            var selectionCells =_this.graph.getSelectionCells();
            if (selectionCells.length > 0) {
              var otherCells = [];
              var cells = _this.graph.getModel().cells;
              var edges = selectionCells[0].edges;
              for(var k in cells) {
                if(cells[k].vertex && cells[k].id !== selectionCells[0].id && cells[k].data && cells[k].data.nodetype !== '.<@END@>.') {
                  if (cells[k].data.nodetype === '.<@START@>.' && selectionCells[0].data.nodetype !== '.<@END@>.') {
                    cells[k].data.actionName = '开始节点';
                    otherCells.push(cells[k].data);
                  } else if (cells[k].data.actionCode) {
                    otherCells.push(cells[k].data);
                  }
                }
              }
              var multipleSelection = _this.getMultipleSelection(edges, true);
              _this.$emit('selectlastcell', selectionCells[0].data, otherCells, multipleSelection);
            }
          }
          // 选择下游(触发)节点
          _this.graph.selectNextCell = function() {
            var selectionCells =_this.graph.getSelectionCells();
            if (selectionCells.length > 0) {
              var otherCells = [];
              var cells = _this.graph.getModel().cells;
              var edges = selectionCells[0].edges;
              var multipleSelection = [];
              for(var k in cells) {
                if(cells[k].vertex && cells[k].id !== selectionCells[0].id && cells[k].data && cells[k].data.nodetype !== '.<@START@>.') {
                  if (cells[k].data.nodetype === '.<@END@>.' && selectionCells[0].data.nodetype !== '.<@START@>.') {
                    cells[k].data.actionName = '结束节点';
                    otherCells.push(cells[k].data);
                  } else if (cells[k].data.actionCode) {
                    otherCells.push(cells[k].data);
                  }
                }
              }
              var multipleSelection = _this.getMultipleSelection(edges, false);
              _this.$emit('selectnextcell', selectionCells[0].data, otherCells, multipleSelection);
            }
          }
          _this.graph.addListener(mxEvent.DOUBLE_CLICK, function (sender, evt) {
            var cell = evt.getProperty('cell');
            if(cell) {
              if(cell.vertex && cell.data){
                if(cell.data && cell.data.nodetype !== ".<@START@>." && cell.data.nodetype !== ".<@END@>."){
                  _this.$emit('celldblclick', cell);
                }
              }
              if(cell.edge){
                cell.data = cell.data || {}
                cell.data.nodetype = 'EDGE'
                _this.$emit('celldblclick', cell);
              }
            }
            return;
          });
          _this.graph.addListener(mxEvent.SELECT, function (sender, evt) {
            var cell = evt.getProperty('cell');
            return;
          });
          _this.graph.addListener(mxEvent.CLICK, function (sender, evt) {
            var cell = evt.getProperty('cell');
            if (!cell || (cell && cell.data)) {
              _this.clearCellOverLay();
              if (!cell) {
                setTimeout(function () {
                  _this.resetCurrentCellStyle();
                  _this.currentCell = null;
                }, 10)
              }
              if(cell && cell.vertex && cell.edges && cell.edges.length > 0) {
                 // 单击节点, 高亮显示节点本身和节点上下游节点
                 setTimeout(function () {
                  _this.resetCurrentCellStyle();
                  _this.currentCell = cell;
                  _this.setCurrentCellStyle();
                }, 10)
                // 增加Overlay
                if(cell.data.nodetype === '+' || cell.data.nodetype === ' ') {
                  return;
                }
                if (cell.data.hisFlag !== '1') { // hisFlag === '1' 表示是历史任务的节点
                  _this.addStateImg(cell, cell.data.jobState);
                  if (cell.data.jobState) {
                    if (cell.data.debug) {
                      _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/remove_breakpoint.png', 16, 16), '取消断点', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(-55, 30), 'remove_breakpoint'));
                    } else {
                      _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/add_breakpoint.png', 16, 16), '添加断点', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(-55, 30), 'add_breakpoint'));
                    }
                    if (!cell.data.retFlag) {
                      _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/add_threshold.png', 16, 16), '设置返回值', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(5, 30), 'add_threshold'));
                    } else {
                      _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/remove_threshold.png', 16, 16), '取消返回值', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(5, 30), 'remove_threshold'));                  
                    }
                  }
                  if (cell.data.jobState === 'C' || cell.data.jobState === 'E' || cell.data.jobState === 'F' || cell.data.jobState === 'I'|| cell.data.jobState === 'U') {
                    _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/redo.png', 16, 16), '重做作业', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(-35, 30), 'redo'));
                    _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/pencil.png', 16, 16), '修改返回值', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(-15, 30), 'force_success'));
                  } else if (cell.data.jobState === 'X') {
                    _this.graph.addCellOverlay(cell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/interrupt_work.png', 16, 16), '中断作业流', mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, new mxPoint(-25, 30), 'interrupt_work'));
                  }
                }
              }
            };
            if (!cell) {
              var selectionCells = _this.graph.getSelectionCells();
              var newSelectionCells = [];
              selectionCells.forEach(function(cell) {
                if (cell.vertex) {
                  newSelectionCells.push(cell);
                }
              });
              _this.graph.setSelectionCells(newSelectionCells);
            }
            return;
          });
          // 图元新增事件
          // _this.graph.addListener(mxEvent.CELLS_ADDED, function (sender, evt) {
          //   const cell = evt.properties.cells[0];
          //   // 如果是线，且没有结束或者开始节点，就直接删除
          //   if (cell && cell.edge) {
          //     if (!cell.target || !cell.source) {
          //       _this.$message({message: '连线起点或终点不能为空!', type: 'warning'});
          //       return false;
          //     }
          //   }
          //   // if (cell.vertex) {
          //   //   // 更新节点样式
          //   //   _this.updateCellImage(evt.getProperty('cells')[0]);
          //   // }
          //   return false;
          // });
          // 连接事件
          _this.graph.addListener(mxEvent.CELL_CONNECTED, function (sender, evt) {
            var edge = evt.getProperty('edge');
            if (edge) {
              if (edge.target && edge.source) {
                if (edge.target.id === edge.source.id) {
                  _this.$message({message: '连线起点和终点不能为同一个节点!', type: 'warning'});
                  _this.graph.removeCells([edge]);
                } else if (edge.target.id == '.<@START@>.') {
                  _this.$message({message: '开始节点不能有入线!', type: 'warning'});
                  _this.graph.removeCells([edge]);
                } else if (edge.source.id == '.<@END@>.') {
                  _this.$message({message: '结束节点不能有出线!', type: 'warning'});
                  _this.graph.removeCells([edge]);
                } else if ((edge.source.id == '.<@START@>.' && edge.target.id == '.<@END@>.')) {
                  _this.$message({message: '开始节点不能直接连接结束节点!', type: 'warning'});
                  _this.graph.removeCells([edge]);
                } else {
                  var cells = _this.graph.getModel().cells;
                  for(var k in cells) {
                    if(cells[k].edge && cells[k].source && cells[k].source.id == edge.source.id && cells[k].target && cells[k].target.id == edge.target.id && cells[k].id !== edge.id) {
                      _this.$message({message: '两个节点之间不允许有多条方向相同的连线!', type: 'warning'});
                      _this.graph.removeCells([edge]);
                      return;
                    }
                  }
                  edge.data = edge.data || {};
                  edge.data.nid = edge.id;
                  edge.data.source = edge.source.id;
                  edge.data.target = edge.target.id;
                  setTimeout(function() {
                    edge.data.style = edge.style;
                    _this.$emit('connected', edge);
                  }, 100)
                }
              } else {
                if (!_this.isLayouting && !_this.isIniting) { // 初始化添加连线和自动布局的时候不判断连线是否有target或source
                  setTimeout(function() {
                    if (!edge.source || !edge.target) { // 需要延迟判断，刚添加连线时edge.target或edge.source为null
                      _this.$message({message: '连线起点或终点不能为空!', type: 'warning'});
                      _this.editorui.undo();
                      _this.editorui.editor.undoManager.history.pop();
                    }
                  }, 10)
                }
              }
            }
          });
          // 节点移动事件，单独拖动连线脱离节点时，提示并撤销操作
          _this.graph.addListener(mxEvent.CELLS_MOVED, function (sender, evt) {
            var cell = evt.properties.cells[0];
            if (cell && cell.edge) {
              if (!cell.target || !cell.source) {
                _this.$message({message: '连线起点或终点不能为空!', type: 'warning'});
                setTimeout(function () {
                  _this.editorui.undo();
                  // 删除历史记录中最后一条
                  _this.editorui.editor.undoManager.history.pop();
                });
                return false;
              }
            } else if (cell && cell.vertex) {
              _this.$emit('movecell', cell);
              // 拖动了节点 则重置它的连线样式(重置连线连接的起始节点和目标节点的锚点)
              if (cell.edges) {
                _this.graph.setCellStyles('edgeStyle', 'orthogonalEdgeStyle', cell.edges);
                _this.graph.setCellStyles('exitX', null, cell.edges);
                _this.graph.setCellStyles('entryX', null, cell.edges);
                _this.graph.setCellStyles('exitY', null, cell.edges);
                _this.graph.setCellStyles('entryY', null, cell.edges);
              }
            }
          });
          // 删除节点或连线师, 触发removecells, 调用方通过removecells事件 删除对应的数据
          _this.graph.addListener(mxEvent.REMOVE_CELLS, function (sender, evt) {
            evt.properties.cells.forEach(function (cell) {
              if (cell) {
                if (cell.vertex) {
                  // 删除节点时 遍历所有的连线，删除无起点或终点连线
                  _this.deleteInvalidEdge();
                }
                // 删除节点后 可能还存在于_this.graph.getModel().cells中, 需要手动删除一下
                var cells = _this.graph.getModel().cells;
                for(var k in cells) {
                  if(cells[k].id === cell.id){
                    delete _this.graph.getModel().cells[k];
                  }
                }
              }
              _this.$emit('removecells', cell);
            })
          });
          // 设置节点提示框
          _this.graph.getTooltipForCell = function(cell){
            if (cell && cell.vertex && cell.data && cell.data.nodetype !== ".<@START@>." && cell.data.nodetype !== ".<@END@>.") {
              return '作业代码:'+ '&nbsp' + (cell.data.actionCode ? cell.data.actionCode : '') + '<br/>' + '作业名称:' + '&nbsp' + (cell.data.actionName ? cell.data.actionName : '') + '<br/>' + '作业类型:' + '&nbsp' + (cell.data.typeCode ? yufp.lookup.convertKey('USE_TASK_JOB_TYPE', cell.data.typeCode) : '') + '<br/>' + '作业状态:' + '&nbsp' + (cell.data.jobState ? yufp.lookup.convertKey('USE_JOB_RUN_STATE', cell.data.jobState) : '') + '<br/>';
            } else if (cell && cell.edge && cell.data) {
              return '返回值范围：' + cell.data.minRet + ' - ' + cell.data.maxRet;
            }
          };
          _this.graph.setCellsResizable(false);
          if(_this.locked){
            _this.lockGraph();
            // var defaultEdgeStyle = _this.graph.stylesheet.getDefaultEdgeStyle();
            // // 锁定状态修改连线颜色值
            // defaultEdgeStyle['strokeColor'] = '#DDDDDD'
          }
          _this.editorui.editor.setModified(false);
        }, function() {
          container.innerHTML = '<center style="margin-top:10%;">Error loading resource files. Please check browser console.</center>';
        });
      },
      // 遍历所有的连线，删除无起点或终点连线
      deleteInvalidEdge: function() {
        var _this = this;
        var cells = _this.graph.getModel().cells;
        for (var c in cells) {
          if (cells[c].edge && (!cells[c].target || !cells[c].source)) {
            _this.graph.removeCells([cells[c]]);
            _this.deleteInvalidEdge();
          }
        }
      },
      // 选中节点右键菜单点击依赖或触发的节点时 取消选中状态的节点，删除对应连线
      deleteEdgeById: function(id) {
        var _this = this;
        var cells = _this.graph.getModel().cells;
        for (var c in cells) {
          if (cells[c].edge && cells[c].id === id) {
            _this.graph.removeCells([cells[c]]);
          }
        }
      },
      getMultipleSelection: function(edges, isSelectlastcell) {
        var multipleSelection = [];
        var type = isSelectlastcell ? 'source' : 'target';
        var selectionCells = this.graph.getSelectionCells();
        if (edges && edges.length > 0) {
          edges.forEach(function(edge) {
            var hasThisData = false;
            var data = edge[type].data;
            multipleSelection.forEach(function(item) {
              if (item.actionCode === data.actionCode || ((item.actionName === '开始节点' || item.actionName === '结束节点') && item.actionName === data.actionName)) {
                hasThisData = true;
              }
            })
            if (!hasThisData && data.actionCode !== selectionCells[0].data.actionCode) {
              multipleSelection.push(data);
            }
          })
        }
        return multipleSelection;
      },
      // 自动布局后连线不是直接(设置edgeStyle为loopEdgeStyle也不行), 只能删除连线后 重新添加连线
      setLayoutEdgesStyle: function() {
        var cellsData = [];
        var edgeCells = [];
        var cells = this.graph.getModel().cells;
        for(var k in cells) {
          if(cells[k].edge) {
            cellsData.push(cells[k].data);
            edgeCells.push(cells[k]);
          }
        }
        this.graph.removeCells(edgeCells);
        this.addEdgeList(null, this.data2EdgeData(cellsData), true);
      },
      data2EdgeData: function(data){
        var cells = [];
        data.forEach(function(item){
          // edgeStyle: elbowEdgeStyle, entityRelationEdgeStyle, loopEdgeStyle, sideToSideEdgeStyle, topToBottomEdgeStyle, orthogonalEdgeStyle, segmentEdgeStyle
          var style = 'edgeStyle=loopEdgeStyle;exitX=1;exitY=0.25;entryX=0;entryY=0.25'; // 直线, 左进右出
          var source = item.source || item.condAction.trim() || '.<@START@>.';
          var target = item.target || item.actionCode.trim() || '.<@END@>.';
          cells.push({
            nodetype: 'EDGE',
            id: source + '-' + target,
            value: '',
            path: item.flowPath,
            remark: item.remark,
            style: style,
            data: item,
            source: source,
            target: target
          })
        })
        return cells;
      },
      // 单击节点，高亮显示节点本身，节点连线，上下游节点
      setCurrentCellStyle: function() {
        var _this = this;
        if (this.currentCell) {
          this.graph.getModel().beginUpdate();
          this.currentCell.edges.forEach(function(edge) {
            // edge.source && _this.graph.addCellOverlay(edge.source, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_s.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
            // edge.target && _this.graph.addCellOverlay(edge.target, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_s.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
            edge.source && _this.graph.setCellStyles('labelBackgroundColor', '#188fff80', [edge.source]);
            edge.target && _this.graph.setCellStyles('labelBackgroundColor', '#188fff80', [edge.target]);
            if (edge.source && edge.source.id === _this.currentCell.id) { // 节点的出线
              _this.graph.setCellStyles('strokeColor', '#da07bd', [edge]);
            } else {
              _this.graph.setCellStyles('strokeColor', '#FB7E22', [edge]);
            }
          });
          this.graph.getModel().endUpdate();
        }
      },
      // 点击其他区域 需要将高亮显示的节点重置到初始状态
      resetCurrentCellStyle: function() {
        var _this = this;
        if (this.currentCell) {
          this.graph.getModel().beginUpdate();
          this.graph.setCellStyles('strokeColor', '#000000', this.currentCell.edges || []);
          this.currentCell.edges.forEach(function(edge) {
            // edge.source && _this.graph.removeCellOverlay(edge.source);
            // edge.target && _this.graph.removeCellOverlay(edge.target);
            edge.source && _this.graph.setCellStyles('labelBackgroundColor', 'transparent', [edge.source]);
            edge.target && _this.graph.setCellStyles('labelBackgroundColor', 'transparent', [edge.target]);
          });
          this.updateEdgeStyle();
          this.graph.getModel().endUpdate();
        }
      },
      // 设置连线锚点(例如: exitX=1;exitY=0.25;entryX=0.25;entryY=1;)
      setEdgeAnchorPoint: function(edgeList) {
        for (var i = 0, length = edgeList.length, edge; i < length; i++) {
          edge = edgeList[i];
          if (edge.edge && edge.data && edge.data.flowPath && edge.source && edge.target) {
            var edgePathArr = edge.data.flowPath.split('L');
            var edgeStartX = edgePathArr[0].split(':')[0];
            var edgeStartY = edgePathArr[0].split(':')[1];
            var edgeEndX = edgePathArr[edgePathArr.length - 1].split(':')[0];
            var edgeEndY = edgePathArr[edgePathArr.length - 1].split(':')[1];
            var exitX = edgeStartX == edge.source.data.positionX ? 0.25 : (edgeStartX > edge.source.data.positionX ? 1 : 0);
            var exitY = edgeStartY == edge.source.data.positionY ? 0.25 : (edgeStartY > edge.source.data.positionY ? 0 : 1);
            var entryX = edgeEndX == edge.target.data.positionX ? 0.25 : (edgeEndX > edge.target.data.positionX ? 1 : 0);
            var entryY = edgeEndY == edge.target.data.positionY ? 0.25 : (edgeEndY > edge.target.data.positionY ? 0 : 1);
            this.graph.setCellStyles('exitX', exitX, [edge]);
            this.graph.setCellStyles('exitY', exitY, [edge]);
            this.graph.setCellStyles('entryX', entryX, [edge]);
            this.graph.setCellStyles('entryY', entryY, [edge]);
          }
        }
      },
      graphComplete: function() {
        // this.graph.fit(); // 自适应
        var _this = this;
        this.graph.center(true,true,0.5,0.5); // 将画布放到容器中间
        // var sc = this.graph.getView().getScale() > 1 ? 1 : 0.8; // 获取当前的缩放比例
        // this.graph.zoomTo(sc);
        this.hideSplash();
        this.editorui.editor.undoManager.history = []; // 避免按ctrl + z 导致自动添加的连线和节点被撤销
        setTimeout(function() {
          _this.isIniting = false;
        }, 500);
      },
      setSidebar: function(items) {
        this.editorui.sidebar.init(items)
      },
      addToolItems: function(items) {
        this.editorui.toolbar.addCustomItems(items)
      },
      getEdgeCellPath: function(cellId, rngMinX, rngMaxY){
        var _this = this;
        var states = this.graph.view.states.map;
        for(var k in this.graph.model.cells){
          var item = this.graph.model.cells[k];
          if(item.edge && states[item.mxObjectId]){
            if (item.id === cellId) {
              var path = states[item.mxObjectId].shape.node.childNodes[1].getAttribute('d');
              // console.log('连线路径 转换前', k, path);
              // console.log('连线路径 转换后', k, _this.convertPath(path, item, rngMinX, rngMaxY));
              return _this.convertPath(path, item, rngMinX, rngMaxY);
            }
          }
        }
      },
      convertPath: function(path, edge, rngMinX, rngMaxY) { 
        // path转换 'M 1390 606 L 1495 606 Q 1505 606 1505 616 L 1505 670.63' => 24:63L54:63L54:44
        var _this = this;
        var pathArr = path.split(' ');
        var isQpath = false;
        var newArr = [];
        var size = this.graph.view.getBackgroundPageBounds();
        var pathArr2 = path.split('M ')[1].split(' L ');
        var exitY = pathArr2[0].split(' ')[1] - size.y;
        var differenceY = exitY - edge.source.geometry.y > 100 ? 400 : 0; // 有时候连线的path的Y坐标减去size.y之后与节点坐标还不能对应,存在一个差值400, 需要减去这个差值
        pathArr.forEach(function(item, index) {
          if (isNaN(Number(item))) {
            isQpath = item == 'Q';
          }
          if (item !== 'M' && !isQpath) {
            if (!isNaN(Number(item)) && (index > 0) && !isNaN(Number(pathArr[index - 1]))) {
              newArr.push(':');
              item = (item - size.y - differenceY - rngMaxY) * -1;
            }
            if (!isNaN(Number(item)) && (index < pathArr.length - 1) && !isNaN(Number(pathArr[index + 1]))) {
              item = item - size.x - rngMinX;
            }
            if (!isNaN(Number(item))) {
              switch (index) {
                case 1:
                  var offset = edge.source.id === '.<@START@>.' ? 5 : 10;
                  if (edge.source.data.positionX < Math.round(item / _this.scale)) {
                    item = edge.source.data.positionX + offset;
                  } else if (edge.source.data.positionX > Math.round(item / _this.scale)) {
                    item = edge.source.data.positionX - offset;
                  } else {
                    item = edge.source.data.positionX;
                  }
                  break;
                case 2:
                  if (edge.source.data.positionY < Math.round(item / _this.scale)) {
                    item = edge.source.data.positionY + 5;
                  } else if (edge.source.data.positionY > Math.round(item / _this.scale)) {
                    item = edge.source.data.positionY - 5;
                  } else {
                    item = edge.source.data.positionY;
                  }
                  break;
                case pathArr.length - 2:
                  var offset = edge.target.id === '.<@END@>.' ? 5 : 10;
                  if (edge.target.data.positionX < Math.round(item / _this.scale)) {
                    item = edge.target.data.positionX + offset;
                  } else if (edge.target.data.positionX > Math.round(item / _this.scale)) {
                    item = edge.target.data.positionX - offset;
                  } else {
                    item = edge.target.data.positionX;
                  }
                  break;
                case pathArr.length - 1:
                  if (edge.target.data.positionY < Math.round(item / _this.scale)) {
                    item = edge.target.data.positionY + 5;
                  } else if (edge.target.data.positionY > Math.round(item / _this.scale)) {
                    item = edge.target.data.positionY - 5;
                  } else {
                    item = edge.target.data.positionY;
                  }
                  break;
                default:
                  item = item / _this.scale;
                  break;
              }
              item = Math.round(item);
            }
            newArr.push(item);
          }
          if (isQpath) {
            if (pathArr[index - 1] === 'Q') {
              item = Math.round((item - size.x - rngMinX) / _this.scale);
              newArr[newArr.length - 3] = item;
            } else if (pathArr[index - 2] === 'Q') {
              item =  Math.round((item - size.y - differenceY - rngMaxY) * -1 / _this.scale);
              newArr[newArr.length - 1] = item;
            }
          }
        });
        return newArr.join('');
      },
      getCellsCoordinate: function() {
        var states = this.graph.view.states.map;
        var cells = this.graph.getModel().cells;
        var size = this.graph.view.getBackgroundPageBounds();
        var xCoordinates = []; 
        var yCoordinates = []; 
        for (var i in cells) {
          var cell = cells[i];
          if(cell.edge && states[cell.mxObjectId]) {
            var path = states[cell.mxObjectId].shape.node.childNodes[1].getAttribute('d');
            var pathArr = path.split(' ');
            var isQpath = false;
            pathArr.forEach(function(item, index) {
              if (isNaN(Number(item))) {
                isQpath = item == 'Q';
              }
              if (!isQpath) {
                if (!isNaN(Number(item)) && (index > 0) && !isNaN(Number(pathArr[index - 1]))) {
                  yCoordinates.push(item - size.y);
                }
                if (!isNaN(Number(item)) && (index < pathArr.length - 1) && !isNaN(Number(pathArr[index + 1]))) {
                  xCoordinates.push(item - size.x);
                }
              }
            })
          }
          if (cell.vertex) {
            xCoordinates.push(cell.geometry.x);
            yCoordinates.push(cell.geometry.y);
          }
        }
        var rngMinX = Math.min.apply(Math, xCoordinates);
        var rngMinY = Math.min.apply(Math, yCoordinates);
        var rngMaxX = Math.max.apply(Math, xCoordinates);
        var rngMaxY = Math.max.apply(Math, yCoordinates);
        return {rngMinX: rngMinX - 50, rngMinY: rngMinY - 50, rngMaxX: rngMaxX + 50, rngMaxY: rngMaxY + 50} 
      },
      // 更新节点信息
      updateCell: function (id, key, value) {
        if (id) {
          var cell = this.getVertex(id);
          if (cell) {
            switch (key) {
            case 'label':
              this.updateCellLabel(cell, value);
              break;
            case 'data':
            case 'id':
              cell[key] = value;
              this.updateCellEdgesId(cell);
              break;
            default:
              key && (cell[key] = value);
              break;
            }
          }
        }
      },
      updateCellEdgesId: function (cell) {
        var edges = cell.edges || [];
        edges.forEach(function(edge) {
          if (edge.data) {
            var condAction = edge.data.condAction != ' ' ? edge.data.condAction : '.<@START@>.';
            var actionCode = edge.data.actionCode != ' ' ? edge.data.actionCode : '.<@END@>.';
            if (edge.target && edge.target.id === cell.id) {
              edge.data.actionCode = cell.id;
              edge.id = condAction + '-' + cell.id;
            } else if (edge.source && edge.source.id === cell.id) {
              edge.data.condAction = cell.id;
              edge.id = cell.id + '-' + actionCode;
            }
          }
        })
      },
      // 更新节点图标
      updateCellImage: function (cell) {
        if (cell) {
          this.updateCellStyle(cell, 'image', 'libs/mxgraph/images/workflow/' + cell.data.typeCode + '.png')
        }
      },
      // 更新节点标记
      updateCellLabel: function (cell, label) {
        var cellName = this.getCellName(label);
        this.graph.getModel().setValue(cell, cellName);
        cell.setValue(cellName);
      },
      // 节点名字超长时 做换行处理
      getCellName(label) {
        return '<div style="width: 160px; font-size: 12px; word-break: break-all; white-space: pre-line;">' + label + '</div>';
      },
      // 更改cell样式
      updateCellStyle: function(cell, type, value) {
        if(cell && type){
          this.graph.setCellStyles(type, value, [cell]);
        }
      },
      // 监控状态下更新连线颜色状态
      updateEdgeStyle: function() {
        var _this = this;
        var cells = this.graph.getModel().cells;
        var edges = [];
        for(var k in cells){
          if(cells[k].edge){
            edges.push(cells[k]);
          }
        }
        this.graph.getModel().beginUpdate();
        edges.forEach(function (edge) {
          if (edge.target) {
            if (edge.target.data && (edge.target.data.jobState === 'X' || edge.target.data.jobState === 'S')) {
              _this.graph.setCellStyles('strokeColor', '#00FF7F', [edge]);
            } else if (edge.target.id === '.<@END@>.' && edge.source && edge.source.data && (edge.source.data.jobState === 'O' || edge.source.data.jobState === 'S')) {
              _this.graph.setCellStyles('strokeColor', '#00FF7F', [edge]);
            }
          }
        });
        this.graph.getModel().endUpdate();
      },
      // 校验节点ID是否已经存在
      checkCellHas: function(id) {
        var cell = this.getVertex(id);
        if(cell){
          return true;
        }
        return false;
      },
      // 锁定视图
      lockGraph: function() {
        var graph = this.graph;
        graph.getModel().beginUpdate();
        try {
          var cells = this.getGraphAllCells();
          graph.toggleCellStyles('locked', 0, cells);
        } finally {
          graph.getModel().endUpdate();
        }
      },
      // 获取所有节点数组
      getGraphAllCells: function() {
        var cells = [], cs = this.graph.getModel().cells;
        for(var k in cs){
          cells.push(cs[k])
        }
        return cells;
      },
      checkValidate: function() {
        var valid = '';
        var _this = this;
        var cells = this.graph.getModel().cells;
        var edges = [], vertexs = [], startCell = null, endCell = null;
        this.resetCurrentCellStyle();
        for(var k in cells) {
          if(cells[k].edge){
            edges.push(cells[k]);
          } else {
            if (k === '.<@START@>.') {
              startCell = cells[k];
            } else if(k==='.<@END@>.'){
              endCell = cells[k];
            } else {
              vertexs.push(cells[k]);
            }
          }
        }
        // 校验开始节点是否有入线
        if (startCell && startCell.edges && startCell.edges.length>0) {
          startCell.edges.forEach(function(edge) {
            if (edge.source.id != startCell.id) {
              valid += '开始节点不能有入线！<br>';
              _this.graph.addCellOverlay(startCell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
            }
          })
        } else {
          valid += '开始节点不能没有出线！<br>';
          _this.graph.addCellOverlay(startCell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
        }
        // 校验结束节点是否有出线
        if (endCell && endCell.edges && endCell.edges.length>0) {
          endCell.edges.forEach(function(edge) {
            if(edge.target.id != endCell.id){
              valid += '结束节点不能有出线！<br>';
          _this.graph.addCellOverlay(endCell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));

            }
          })
        } else {
          valid += '结束节点不能没有入线！<br>';
          _this.graph.addCellOverlay(endCell, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
        }
        // 校验孤立节点
        var illegalCells = [];
        var illegalPastedCells = [];
        vertexs.forEach(function(v) {
          if (v.geometry) {
            if (!v.edges || v.edges.length < 2) {
              illegalCells.push(v.value);
              _this.graph.addCellOverlay(v, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
            } else if (v.edges.length > 1) {
              // 校验任一非开始节点和结束节点，必须有进有出连线
              var hasIn = false, hasOut = false;
              for(var i=0,l=v.edges.length, e;i<l;i++){
                e = v.edges[i];
                if(e.target && e.target.id == v.id){
                  hasOut = true;
                }
                if(e.source && e.source.id == v.id){
                  hasIn = true;
                }
              }
              if (!hasIn || !hasOut) {
                illegalCells.push(v.value);
                _this.graph.addCellOverlay(v, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
              } else {
                _this.graph.removeCellOverlays(v);
              }
            }
          }
          if (v.isPasted) { // 是复制的节点 还未双击 重新设置actionCode
            illegalPastedCells.push(v.value);
            _this.graph.addCellOverlay(v, _this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), 'warning', mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_TOP));
          }
        })
        if (illegalCells.length > 0) {
          valid += illegalCells.join('、') + '节点不合法，至少要有一条出线和一条入线！<br>';
        }
        if (illegalPastedCells.length > 0) {
          valid += '复制的' + illegalPastedCells.join('、') + '节点不合法，复制的节点必须修改作业代码，确保作业代码的唯一性！';
        }
        return valid;
      },
      // 校验流程图是否合法
      checkEdgeOfCells: function (cell) {
        var hasEdge = false;
        var cells = this.graph.getModel().cells;
        for (var k in cells) {
          if (cells[k].edge && cells[k].source && cells[k].target && cells[k].id !== cell.id) {
            if ((cells[k].source.id === cell.source.id && cells[k].target.id === cell.target.id) || (cells[k].source.id === cell.target.id && cells[k].target.id === cell.source.id)) {
              hasEdge = true;
              break;
            }
          }
        }
        return hasEdge;
      },
      _resetGraphSettings: function() {
        // #TODO 根据当前图类型及状态，配置对应的默认信息
        this.graph.setCellsResizable(false); //禁止改变节点大小
        this.graph.foldingEnabled = false; //节点不可展开
        this.graph.setCellsEditable(false); //节点不可编辑
        this.graph.setTooltips(false); //鼠标移入节点不展示节点信息
        this.graph.setDropEnabled(true); //从工具栏拖动到目标细胞时细胞边界是否产生光圈
        mxGraphHandler.prototype.guidesEnabled = true; //显示细胞位置标尺
        this.graph.center(true,true,0.5,0.5); // 将画布放到容器中间
        var sc = this.graph.getView().getScale(); // 获取当前的缩放比例
        this.graph.zoomTo(Math.round(sc/2)); // 在缩放一半，否则是满屏状态，不好看
      },
      updateWorkStates: function(states, list) {
        var model = this.graph.getModel();
        model.beginUpdate();
        try {
          for (var i = 0; i < states.length; i++) {
            // Processes the activity nodes inside the process node
            var id = states[i].serverId;
            var state = states[i].state;
            
            // Gets the cell for the given activity name from the model
            var cell = model.getCell(id);
            
            // Updates the cell color and adds some tooltip information
            if (cell != null)
            {
              // Resets the fillcolor and the overlay
              // this.graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, 'white', [cell]);
              this.graph.removeCellOverlays(cell);
  
              // Changes the cell color for the known states
              if (state == 'warning') {
                this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/lightbulb_on.png', 16, 16), state, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP));
              } else if (state == 'running') {
                // this.graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, '#fff2cc', [cell]);
                this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/check.png', 16, 16), state, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP));
              } else if (state == 'stoping') {
                // this.graph.setCellStyles(mxConstants.STYLE_FILLCOLOR, '#d4e1f5', [cell]);
                this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/forbidden.png', 16, 16), state, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP));
              }
            }
          } // for
          if (list && list.length > 0) {
            for (var i = 0; i < list.length; i++) {
              if (list[i].data && list[i].nodetype !== '.<@START@>.' && list[i].nodetype !== '.<@END@>.') {
                var id = list[i].id;
                var state = list[i].data.jobState;
                var cell = model.getCell(id);
                if (cell != null) {
                  this.graph.removeCellOverlays(cell);
                  this.addStateImg(cell, state);
                }
              }
            }
          }
        } finally {
          model.endUpdate();
        }
      },
      addStateImg: function(cell, state) {
        if (state == 'S') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_s.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'X') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_x.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'U') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_u.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'Q') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_q.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'O') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_o.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'F') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_f.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'I') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_i.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'C') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_c.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        } else if (state == 'E') {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), state, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP));
        }
        // 已设置断点的 节点前面显示一个红叉 表示运行到此处会停下
        if (cell.data && cell.data.debug) {
          this.graph.addCellOverlay(cell, this.createOverlay(new mxImage(mxBasePath + '/images/overlays/status_e.gif', 16, 16), '断点', mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE));
        }
      },
      createOverlay: function (image, tooltip, align, verticalAlign, point, type) {
        var _this = this;
        var overlay = new mxCellOverlay(image, tooltip, align || mxConstants.ALIGN_RIGHT, verticalAlign || mxConstants.ALIGN_BOTTOM, point || new mxPoint(0, 0), 'pointer');
        overlay.addListener(mxEvent.CLICK, function(sender, evt) {
          _this.$emit('overlayclick', sender, evt.properties.cell);
        });
        overlay.type = type;
        return overlay;
      },
      layout: function () {
        if (!this.isLayouting) {
          var _this = this;
          var status = this.checkValidate();
          if (status === '') {
            this.isLayouting = true;
            this.organicLayout();
            this.autoFit();
            this.setLayoutEdgesStyle();
            setTimeout(function() {
              _this.isLayouting = false;
            }, 500);
          } else {
            this.$msgbox({
              title: '提示',
              message: status,
              dangerouslyUseHTMLString: true,
              confirmButtonText: '确定'
            });
          }
        }
      },
      autoFit: function() {
        // this.graph.fit(); // 自适应
        this.graph.center(true,true,0.5,0.5); // 将画布放到容器中间
        var sc = this.graph.getView().getScale(); // 获取当前的缩放比例
        sc = sc > 1 ? 1 : 0.8
        this.graph.zoomTo(sc); // 缩放
      },
      organicLayout: function () {
        var _this = this;
        this.graph.getModel().beginUpdate();
        var parent = this.graph.getDefaultParent();
				try {
          var layout = new mxHierarchicalLayout(this.graph);
          layout.disableEdgeStyle = false; // 是否禁用线条的样式, 默认为true
          layout.orientation = mxConstants.DIRECTION_WEST;
          layout.execute(parent);
        } catch (e) {
          throw e;
        } finally {
          _this.graph.getModel().endUpdate();
        }
      },
      // 清除节点运行图标
      clearCellOverLay: function() {
        var _this = this;
        var cells = this.graph.getModel().cells;
        var cell = null;
        for(var k in cells){
          cell = cells[k];
          if(cell && cell.overlays){
            var overlays = cell.overlays;
            for(var i = overlays.length - 1; i >= 0; i--){
              if (['S', 'X', 'U', 'W', 'Q', 'O', 'F', 'I', 'C'].indexOf(overlays[i].tooltip) === -1 && overlays[i].verticalAlign!=='top'){
                _this.graph.removeCellOverlay(cell, overlays[i]);
              }
            }
          }
        }
      },
      /**
       * @description 添加节点
       * @param {Object} parent 父节点
       * @param {String} id 节点ID
       * @param {String} value 节点显示值
       * @param {Number} x 节点x轴位置
       * @param {Number} y 节点y轴位置
       * @param {Number} width 节点宽度
       * @param {Number} height 节点高度
       * @param {String} style 节点样式字符串
       * @param {Boolean} relative 是否相对位置，默认false
       * @param {Object} data 节点数据属性
       */
      addVertex: function (parent, id, value, x, y, width, height, style, relative, data) {
        parent = parent || this.graph.getDefaultParent();
        this.graph.getModel().beginUpdate();
        try {
          var v1 = this.graph.insertVertex(
            parent,
            id,
            value,
            x || 100,
            y || 200,
            width || 160,
            height || 56,
            // 'custom-style-' + data.serverType,
            style,
            relative
          );
          data = data || {};
          data.nodetype = data.nodetype || value;
          v1.data = data;
          this.addCellOverlay(v1);
        } finally {
          this.graph.getModel().endUpdate();
          return v1;
        }
      },
      /**
       * @description 批量增加节点
       * @param {Object} parent 父节点
       * @param {Array<Object>} list 节点列表 
       */
      addVertexList: function(parent, list) {
        var _this = this;
        parent = parent || this.graph.getDefaultParent();
        this.graph.getModel().beginUpdate();
        var nodeList = [];
        try {
          // var b = this.graph.getView().graphBounds;
          for(var i = 0, l = list.length, node, tmpData, cell, cellName; i < l; i++) {
            node = list[i];
            cellName = _this.getCellName(node.value);
            cell = this.graph.insertVertex(
              parent,
              node.id,
              cellName,
              node.x,
              node.y,
              node.width,
              node.height,
              node.style,
              node.relative
            )
            tmpData = node.data || {};
            tmpData.nodetype = node.nodetype || node.value;
            cell.data = tmpData
            nodeList.push(cell);
          }
        } finally {
          this.graph.getModel().endUpdate();
          return nodeList;
        }
      },
      /**
       * @description 增加连线
       * @param {Object} parent 容器节点
       * @param {Object} edge 连线对象 
       */
      addEdge: function(parent, edgeObj) {
        parent = parent || this.graph.getDefaultParent();
        this.graph.getModel().beginUpdate();
        var edge;
        try {
          edge = this.graph.insertEdge(
            parent,
            edgeObj.id,
            edgeObj.value || '',
            this.getVertex(edgeObj.source),
            this.getVertex(edgeObj.target),
            edgeObj.style || ''
          );
        } catch(e) {
          console.log('添加连线异常', edge);
        } finally {
          this.graph.getModel().endUpdate();
          return edge;
        }
      },
      /**
       * @description 批量增加连线
       * @param {Object} parent 容器节点
       * @param {Array<Object>} list 连线列表 
       */
      addEdgeList: function(parent, list, notSetEdgeAnchorPoint, vertexList) {
        var edgeList = [];
        var cells = vertexList || this.graph.getModel().cells;
        parent = parent || this.graph.getDefaultParent();
        this.graph.getModel().beginUpdate();
        try {
          for(var i = 0, l = list.length, edge, tmpData; i < l; i++) {
            edge = list[i];
            var cellEdge = this.graph.insertEdge(
              parent,
              edge.id,
              edge.value || '',
              this.getVertex(edge.source, cells),
              this.getVertex(edge.target, cells),
              edge.style || ''
            )
            tmpData = edge.data || {};
            tmpData.nodetype = edge.nodetype || edge.value;
            cellEdge.data = JSON.parse(JSON.stringify(tmpData));
            edgeList.push(cellEdge);
          }
        } catch(e) {
          console.log('添加连线异常', e);
        } finally {
          if (!notSetEdgeAnchorPoint) { // 导入的老数据（newFlag为0）需要计算锚点，newFlag为1 和 自动布局时不需要计算锚点
            this.setEdgeAnchorPoint(edgeList);
          }
          this.graph.getModel().endUpdate();
          return edgeList;
        }
      },
      getVertex: function (id, cells) {
        cells = cells || this.graph.getModel().cells;
        for (var k in cells) {
          if (cells[k].id === id) {
            return cells[k];
          }
        }
      },
      // 对图节点排序
      orderGraph: function () {
        var cells = this.graph.getModel().cells;
        var tmpCells = [];
        for (var k in cells) {
          tmpCells.push(cells[k]);
        }
        this.graph.orderCells(false, tmpCells);
      },
      // 导出xml文件
      exportXML: function () {
        var status = this.checkValidate();
        if (status === '') {
          this.orderGraph();
          var encoder = new mxCodec();
          var node = encoder.encode(this.graph.getModel());
          return mxUtils.getPrettyXml(node);
        } else {
          this.$msgbox({
            title: '提示',
            message: status,
            dangerouslyUseHTMLString: true,
            confirmButtonText: '确定'
          });
        }
      },
      // 加载xml文件
      loadXML: function (xml) {
        var doc = mxUtils.parseXml(xml);
        this.graph.importGraphModel(doc.documentElement);
      },
      hiddenOutlineWindow: function (isReopen) {
        if (isReopen === false) {
          if (this.editorui.actions.outlineWindow && this.editorui.actions.outlineWindow.window.isVisible() === true) {
            this.editorui.actions.outlineWindow.window.setVisible(isReopen);
            this.outlineHiddenClick = true;
          }
        } else if (this.outlineHiddenClick) {
          if (this.editorui.actions.outlineWindow && this.editorui.actions.outlineWindow.window.isVisible() === false) {
            this.editorui.actions.get('outline').funct();
            this.outlineHiddenClick = false;
          }
        }
      },
      clearGraph: function() {
        this.graph && this.graph.removeCells(this.graph.getChildVertices(this.graph.getDefaultParent()));
      }
    }
  });
}(Vue, 'yu-workflow'));